#include "def.h"
#include "util.h"
#include "..\CommLib\PacketConven.h"
#include <Maya/MFn.h>
#include <Maya/MFnMesh.h>
#include <Maya/MItDependencyNodes.h>
#include <Maya/MObjectArray.h>
#include <Maya/MIntArray.h>
#include <Maya/MPlug.h>
#include <Maya/MPlugArray.h>
#include <Maya/MFnTypedAttribute.h>
#include <Maya/MGlobal.h>
#include <Maya/MDagPath.h>
#include <Maya/MFnMesh.h>

/**
	Determines if an object handle is a mesh

	@author Dacre Denny
	@param object the object to be tested
	@return true if object is a mesh
*/
bool VUtil::IsMesh(MObject& object) {

	return !object.isNull() && object.apiType() == MFn::kMesh;
}

/**
	Determines if an object handle is a transform

	@author Dacre Denny
	@param object the object to be tested
	@return true if object is a transform
*/
bool VUtil::IsTransform(MObject& object) {

	return !object.isNull() && object.apiType() == MFn::kTransform;
}

/**
	Determines if an object handle is a recognized light

	@author Dacre Denny
	@param object the object to be tested
	@return true if object is a recognized light
*/
bool VUtil::IsLight(MObject& object) {

	return !object.isNull() && 
		(object.apiType() == MFn::kPointLight ||
		object.apiType() == MFn::kDirectionalLight ||
		object.apiType() == MFn::kSpotLight);
}

/**
	Determines if a sampler follows the naming convention for an intance sampler

	@author Dacre Denny
	@param object the sampler object to be tested
	@return true if satisfied, otherwise false
*/
bool VUtil::IsInstanceSampler(MObject& object) {

	if(VUtil::IsTextureSupported(object)) {

		MString samplerNameString;
		VUtil::GetObjectName(object, samplerNameString);
	
		MStringArray samplerNameArray;
		samplerNameString.split('_', samplerNameArray);

		if(samplerNameArray.length() == 4) {

			return (samplerNameArray[0] == MString("lv"));
		}
	}

	return false;
}

/**
	Gets the transformation object associated with this mesh object if the a mesh is valid
	and the transformation object exists

	@author Dacre Denny
	@param object the mesh object whose transform is being aquired
	@param transform the resulting transformation object
	@return true if successful otherwise false
*/
bool VUtil::GetMeshTransform(MObject& object, MObject& transform) {

	if(!object.hasFn(MFn::kMesh))
		return false;

	return VUtil::GetObjectTransform(object, transform);
}

/**
	Gets the transformation object associated with this object

	@author Dacre Denny
	@param object the object whose transform is being aquired
	@param transform the resulting transformation object
	@return true if successful otherwise false
*/
bool VUtil::GetObjectTransform(MObject& object, MObject& transform) {

	if(object.isNull())
		return false;

	MStatus result;
	MFnDagNode dn(object, &result);
	MDagPath path;
	MAYA_RETURN_IF_FAIL(dn.getPath(path), false);

	for(uint i = 0, n = path.length(); i < n; i++) {

		MObject node = path.node();
		if(node.hasFn(MFn::kTransform)) {
			transform = node;
			return true;
		}

		path.pop(1);
	}

	MAYA_RETURN_IF_FAIL(result, false);

	for(uint i = 0; i < dn.parentCount(); i++) {

		MObject parentObject = dn.parent(i, &result);
		MAYA_CONTINUE_IF_FAIL(result);

		if(parentObject.hasFn(MFn::kTransform)) {
			transform = parentObject;
			return true;
		}
	}

	return false;
}

/**
	Gets the first mesh object associated with this transform object if the transform is valid

	@author Dacre Denny
	@param object the transform object whose mesh is being aquired
	@param mesh the resulting mesh object
	@return true if successful otherwise false
*/
bool VUtil::GetTransformMesh(MObject& object, MObject& mesh) {

	return GetTransformChild(object, mesh, MFn::kMesh);
}

/**
	Gets the first recognized light object associated with this transform object if the transform is valid

	@author Dacre Denny
	@param object the transform object whose mesh is being aquired
	@param light the resulting light object
	@return true if successful otherwise false
*/
bool VUtil::GetTransformLight(MObject& object, MObject& light) {

	if(GetTransformChild(object, light, MFn::kPointLight))
		return true;
	if(GetTransformChild(object, light, MFn::kDirectionalLight))
		return true;
	if(GetTransformChild(object, light, MFn::kSpotLight))
		return true;

	return false;
}

/**
	Gets the first object of the specified type that is associated with this transform object if the transform is valid

	@author Dacre Denny
	@param object the transform object whose mesh is being aquired
	@param child the resulting child object
	@param type the API's type that the child object must posses
	@return true if successful otherwise false
*/
bool VUtil::GetTransformChild(MObject& object, MObject& child, MFn::Type type) {

	if(!object.hasFn(MFn::kTransform))
		return false;

	MStatus result;
	MFnDagNode dn(object, &result);
	MAYA_RETURN_IF_FAIL(result, false);

	for(uint i = 0; i < dn.childCount(); i++) {

		MObject childObject = dn.child(i, &result);
		MAYA_CONTINUE_IF_FAIL(result);

		if(childObject.hasFn(type)) {
			child = childObject;
			return true;
		}
	}

	return false;
}

/**
	Gets the triangle count of the object if it is a mesh that is valid

	@author Dacre Denny
	@param object the object to be queried
	@param triangleCount the integer to store the triangle count
	@return true if object is a valid mesh otherwise false
*/
bool VUtil::GetMeshTriangleCount(MObject& object, u_int& triangleCount) {

	if(!VUtil::IsMesh(object)) 
		return false;
	
	MStatus result;
	MFnMesh objectMesh(object, &result);
	MAYA_RETURN_IF_FAIL(result, false);

	MIntArray objectPolygons;
	MAYA_RETURN_IF_FAIL(objectMesh.getTriangles(objectPolygons, MIntArray()), false);

	uint count = 0;
	for(u_int i = 0; i < objectPolygons.length(); i++)
		count += objectPolygons[i];

	triangleCount = count;
	return true;
}

/**
	Gets the materials of the object if it is a mesh that has materials

	@author Dacre Denny
	@param object the object to be tested
	@param materials the object array to store the materials
	@return true if object is a mesh and has materials
*/
bool VUtil::GetMeshMaterials(MObject& object, MObjectArray& materials) {

	if(VUtil::IsMesh(object)) {

		MFnMesh mesh(object);
	
		MObjectArray shaderEngines;
		MAYA_RETURN_IF_FAIL(mesh.getConnectedShaders(0, shaderEngines, MIntArray()), false);

		if(shaderEngines.length() == 0)
			return false;
		
		MStatus result;
		MFnDependencyNode shaderEngine(shaderEngines[0], &result);
		MAYA_RETURN_IF_FAIL(result, false);

		MPlug surfacePlug = shaderEngine.findPlug(MString("surfaceShader"), true, &result);
		MAYA_RETURN_IF_FAIL(result, false);

		MPlugArray plugArray;
		surfacePlug.connectedTo(plugArray,true,true,&result);
		MAYA_RETURN_IF_FAIL(result, false);

		for(u_int i = 0; i < plugArray.length(); i++) {
			
			MObject surfaceMaterial = plugArray[i].node(&result);
			MAYA_CONTINUE_IF_FAIL(result);

			if(VUtil::IsMaterialSupported(surfaceMaterial)) {
				materials.append(surfaceMaterial);
			}
		}
		return (materials.length() > 0);
	}
	return false;
}

/**
	Gets a single material of the object if it is a mesh that has materials. The material
	is specified by a zero based index

	@author Dacre Denny
	@param object the object to be tested
	@param index the material index to retrieve
	@param material the object to store the material

	@return true if object is a mesh that has the indexed material
*/
bool VUtil::GetMeshMaterial(MObject& object, u_int index, MObject& material) {

	MObjectArray materials;

	if(VUtil::GetMeshMaterials(object, materials)) {

		if(index < materials.length()) {
			material = materials[index];
			return true;
		}
	}
	return false;
}

int VUtil::GetMeshPartCount(MObject& object) {
	
	if(VUtil::IsMesh(object)) {

		MFnMesh mesh(object);
		MObjectArray shaderEngines;
		MAYA_RETURN_IF_FAIL(mesh.getConnectedShaders(0, shaderEngines, MIntArray()), false);

		return shaderEngines.length();
	}

	return 0;
}

/**
	Returns the vertex format of a mesh object. The function only succedes if
	the object is valid and a mesh, and if the required attributes exist on it

	@author Dacre Denny
	@param object the object to be analyised
	@param vertexFormat reference to the varaible that will hold the objects vertex format

	@return true if object is a mesh and consists of required attribute otherwise false
*/
//bool VUtil::GetMeshVertexFormat(MObject& object, u_int& vertexFormat) {
//
//	if(!VUtil::IsMesh(object))
//		return false;
//	
//	MPlug attribute;
//	if(!VUtil::GetObjectAttribute(object, MString("lv_format"), attribute))
//		return false;
//
//	int vertexFormatString;
//	if(MAYA_FAIL(attribute.getValue(vertexFormatString)))
//		return false;
//
//	MStringArray vertexFormatArray;
//	if(MAYA_FAIL(vertexFormatString.split(',', vertexFormatArray)))
//		return false;
//		
//	vertexFormat = 0;
//	for(uint i = 0; i < vertexFormatArray.length(); i++) {
//
//		const MString formatElement = vertexFormatArray[i];
//		if(formatElement == "P")
//			vertexFormat |= VERTEX_POSITION;
//		else if(formatElement == "N")
//			vertexFormat |= VERTEX_NORMAL;
//		else if(formatElement == "T")
//			vertexFormat |= VERTEX_TANGENT;
//		else if(formatElement == "UV0")
//			vertexFormat |= VERTEX_UV0;
//		else if(formatElement == "UV1")
//			vertexFormat |= VERTEX_UV1;
//		else if(formatElement == "UV2")
//			vertexFormat |= VERTEX_UV2;
//		else if(formatElement == "C")
//			vertexFormat |= VERTEX_COLOR;
//		else if(formatElement == "D")
//			vertexFormat |= VERTEX_DEFORMATION;
//	}
//
//	return true;
//}

/**
	Attempts to locate a procedural texture object from the specified mesh object. The result is based on the specified
	channel of which are:
	<ul>
	<li>"INSTANCE_POSITION"</li>
	<li>"INSTANCE_ROTATION"</li>
	<li>"INSTANCE_SCALE"</li>
	</ul>

	The function will only succeed if it can return a procedural texture object that is currently supported

	@author Dacre Denny
	@param targetObject the mesh object that has associated with it a procedural texture object on a channel of interest
	@param channel the channel of interest in the aquired texture object
	@param proceduralTexture the object to store the texture

	@return true if object is a mesh and it has a valid procedural texture on the specified channel otherwise false
*/
bool VUtil::GetMeshInstanceProcedrual(MObject& targetObject, MString& channel, MObject& proceduralTexture) {

#pragma message ("this function no longer needed?")
	if(!VUtil::IsMesh(targetObject))
		return false;

	MPlug channelPlug;
	if(!VUtil::GetObjectAttribute(targetObject, channel, channelPlug))
		return false;

	MString materialName;
	MAYA_RETURN_IF_FAIL(channelPlug.getValue(materialName),false);
	
	MObject materialObject;
	if(!VUtil::GetProceduralByName(materialName, materialObject))
		return false;
	
	if(!VUtil::IsMaterialSupported(materialObject))
		return false;
	
	MStatus result;
	MFnDependencyNode materialDN = MFnDependencyNode(materialObject, &result);
	MAYA_RETURN_IF_FAIL(result,false);

	MPlug colorPlug = materialDN.findPlug(MString("color"), &result);
	MAYA_RETURN_IF_FAIL(result,false);
	
	MPlugArray plugArray;
	materialDN.getConnections(plugArray);
	colorPlug.connectedTo(plugArray, true, true, &result);
	MAYA_RETURN_IF_FAIL(result,false)

	for(u_int i = 0; i < plugArray.length(); i++) {

		MFnDependencyNode plugDN = MFnDependencyNode(plugArray[i].node(), &result);
		MAYA_CONTINUE_IF_FAIL(result);

		if(VUtil::IsTextureSupported(plugDN.object())) {
			
			proceduralTexture = plugDN.object();
			return true;
		}
	}

	return false;
}

/**
	Determines if the specified object is a material and if it
	is currently supported

	@author Dacre Denny
	@param object the object to be tested

	@return true if object is a materal that is supported
*/
bool VUtil::IsMaterialSupported(MObject& object) {

	return object.hasFn(MFn::kLambert) || object.hasFn(MFn::kPhong) || object.hasFn(MFn::kBlinn);
}

/**
	Attempts to retrieve a mesh object from the current scene, whose name
	equals the name specified.

	@author Dacre Denny
	@param name the name of the object being sought
	@param targetObject the respective object if it exists

	@return true if object of the specified name is found and it is of type mesh, otherwise false
*/
bool VUtil::GetMeshByName(MString& name, MObject& targetObject) {
	
	MItDependencyNodes meshIterator(MFn::kMesh);
	for(; !meshIterator.isDone(); meshIterator.next()) {
		
		MStatus result;
		MFnDependencyNode meshDN(meshIterator.item(), &result);
		MAYA_CONTINUE_IF_FAIL(result);

		if(meshDN.name() == name && meshIterator.item().hasFn(MFn::kMesh)) {
			targetObject = meshIterator.item();			
			return true;
		}
	}

	return false;
}

/**
	Attempts to retrieve a material object from the specified part of the specifid mesh object.

	@author Dacre Denny
	@param object the targetmesh to examine
	@param index the part index of the target mesh
	@param material the respective material if it exists

	@return true if object of the specified part material is found, otherwise false
*/
bool VUtil::GetPartMaterial(MObject& object, uint index, MObject& material) {

	if(!VUtil::IsMesh(object))
		return false;
	
	MStatus result;
	MFnMesh objectMesh(object, &result);

	MObjectArray shaderEngines;
	MAYA_RETURN_IF_FAIL(objectMesh.getConnectedShaders(0, shaderEngines, MIntArray()), false);

	const int shaders = shaderEngines.length();
	if(shaders == 0)
		return false;
	
	if(index < shaders && index >= 0) {

		MStatus result;
		MFnDependencyNode shaderEngine(shaderEngines[index], &result);
		MAYA_RETURN_IF_FAIL(result, false);

		MPlug surfacePlug = shaderEngine.findPlug(MString("surfaceShader"), true, &result);
		MAYA_RETURN_IF_FAIL(result, false);

		MPlugArray plugArray;
		surfacePlug.connectedTo(plugArray,true,true,&result);
		MAYA_RETURN_IF_FAIL(result, false);

		for(int i = 0; i < plugArray.length(); i++) {

			MObject surfaceMaterial = plugArray[i].node(&result);
			MAYA_CONTINUE_IF_FAIL(result);

			if(VUtil::IsMaterialSupported(surfaceMaterial)) {
				
				material = surfaceMaterial;
				return true;
			}
		}
	}

	return false;
}

/**
	Attempts to retrieve a material object from the current scene, whose name
	equals the name specified.

	@author Dacre Denny
	@param name the name of the object being sought
	@param materialObject the respective object if it exists

	@return true if object of the specified name is found and it is of a supported material type, otherwise false
*/
bool VUtil::GetMaterialByName(MString& name, MObject& materialObject) {
	
	MFn::Type materailTypes[] = {MFn::kLambert, MFn::kPhong, MFn::kBlinn};

	for(u_int i = 0; i < 3; i++) {
		
		MItDependencyNodes materialIterator(materailTypes[i]);
		for(; !materialIterator.isDone(); materialIterator.next()) {
			
			MStatus result;
			MFnDependencyNode materialDN(materialIterator.item(), &result);
			MAYA_CONTINUE_IF_FAIL(result);

			if(materialDN.name() == name) {

				if(VUtil::IsMaterialSupported(materialIterator.item())) {
					materialObject = materialIterator.item();
					return true;
				}

				return false;
			}
		}
	}
	return false;
}

/**
	Attempts to retrieve a procedrual/texture object from the current scene, whose name
	equals the name specified. First match is retrieved. Only supported procedurals will
	be returned.

	@author Dacre Denny
	@param name the name of the object being sought
	@param proceduralTextureObject the respective object if it exists

	@return true if object of the specified name is found and it is of a supported texture type, otherwise false
*/
bool  VUtil::GetProceduralByName(MString& name, MObject& proceduralTextureObject) {
	
	#pragma message ("globalize these and add global count for loops etc")
	MFn::Type proceduralTypes[] = {MFn::kChecker, MFn::kNoise, MFn::kGrid, MFn::kRamp};

	for(u_int i = 0; i < 4; i++) {
		
		MItDependencyNodes textureIterator(proceduralTypes[i]);
		for(; !textureIterator.isDone(); textureIterator.next()) {
			
			MStatus result;
			MFnDependencyNode proceduralDN(textureIterator.item(), &result);
			MAYA_CONTINUE_IF_FAIL(result);

			if(proceduralDN.name() == name) {

				if(VUtil::IsTextureSupported(textureIterator.item())) {
					proceduralTextureObject = textureIterator.item();
					return true;
				}

				return false;
			}
		}
	}
	return false;
}

/**
	Attempts to demystify the name of the passed sampler object. It looks for the naming convention used in
	standard samplers and breaks the string down accordingly

	@author Dacre Denny
	@param object the sampler object to analyse
	@param targetMesh the target mesh of this sampler
	@param pPartIndex optional pointer to store the target part index that this sampler targets in the target mesh
	@param targetChannel the channel that this sampler targets

	@return true if successful otherwise false
*/
bool VUtil::GetStandardSamplerTokens(MObject& object, MObject& targetMesh, UINT* pPartIndex, MString& targetChannel) {

	if(!VUtil::IsTextureSupported(object))
		return false;

	if(VUtil::IsInstanceSampler(object))
		return false;

	MString samplerName;
	if(!VUtil::GetObjectName(object, samplerName))
		return false;

	MStringArray samplerNameArray;
	samplerName.split('_', samplerNameArray);

	if(samplerNameArray.length() != 5)
		return false;

	VUtil::GetMeshByName(samplerNameArray[0], targetMesh);
	if(pPartIndex)
		*pPartIndex = (UINT)samplerNameArray[1].asInt();
	
	targetChannel = MString("liveview_") + samplerNameArray[3];
	return true;	
}

/**
	Attempts to demystify the name of the passed sampler object. It looks for the naming convention used for
	samplers used in instancing and breaks the string down accordingly

	@author Dacre Denny
	@param object the sampler object to analyse
	@param targetName the target mesh of this instance sampler
	@param childName the child mesh of this instance sampler
	@param targetChannel the instancing channel that this sampler is used for

	@return true if successful otherwise false
*/
bool VUtil::GetInstanceSamplerTokens(MObject& object, MObject& targetMesh, MObject& targetChild, MString& targetChannel) {

	if(!VUtil::IsTextureSupported(object))
		return false;

	if(!VUtil::IsInstanceSampler(object))
		return false;

	MString samplerName;
	if(!VUtil::GetObjectName(object, samplerName))
		return false;

	MStringArray samplerNameArray;
	samplerName.split('_', samplerNameArray);

	if(samplerNameArray.length() != 4)
		return false;

	if(!VUtil::GetMeshByName(samplerNameArray[1], targetMesh))
		return false;

	if(!VUtil::GetMeshByName(samplerNameArray[2], targetChild))
		return false;

	switch(samplerNameArray[4].asInt()) {

		case 0:
			targetChannel = MString("lv_position");
			break;
		case 1:
			targetChannel = MString("lv_scale");
			break;
		case 2:
			targetChannel = MString("lv_rotation");
			break;
		default:
			return false;
	}

	return true;
}

/**
	Attempts to retrieve a procedrual/texture object from the current scene, whose name
	equals the name specified. First match is retrieved. Only supported procedurals will
	be returned. Texture's are also returned.

	@author Dacre Denny
	@param name the name of the object being sought
	@param proceduralTextureObject the respective object if it exists

	@return true if object of the specified name is found and it is of a supported texture type, otherwise false
*/
bool VUtil::GetSamplerByName(MString& name, MObject& surfaceTextureObject) {
	
	#pragma message ("globalize these and add global count for loops etc")

	MFn::Type returnTypes[] = {MFn::kChecker, MFn::kNoise, MFn::kGrid, MFn::kRamp, MFn::kFileTexture, MFn::kLayeredTexture};

	for(u_int i = 0; i < 6; i++) {
		
		MItDependencyNodes textureIterator(returnTypes[i]);
		for(; !textureIterator.isDone(); textureIterator.next()) {
			
			MStatus result;
			MFnDependencyNode proceduralDN(textureIterator.item(), &result);
			MAYA_CONTINUE_IF_FAIL(result);

			MString dnName = proceduralDN.name();
			if(dnName == name) {

				if(VUtil::IsTextureSupported(textureIterator.item())) {
					surfaceTextureObject = textureIterator.item();
					return true;
				}

				return false;
			}
		}
	}
	return false;
}

/**
	Determines if the specified object is a texture and if so,
	wether or not the texture is currently supported for interpretation

	@author Dacre Denny
	@param object the object to be tested

	@return true if object is a texture that is supported
*/
bool VUtil::IsTextureSupported(MObject& object) {

	return object.hasFn(MFn::kChecker) || object.hasFn(MFn::kNoise) || object.hasFn(MFn::kGrid) || object.hasFn(MFn::kRamp) || object.hasFn(MFn::kFileTexture) || object.hasFn(MFn::kLayeredTexture);
}

/**
	Attempts to aquire the first node in the root's dag tree that
	is discovered to equal the function type specified.

	@author Dacre Denny
	@param root the root object that is suspected to have a child node of the specified type
	@param type the child node that is being searched for
	@param the object of the result if it is found

	@return true if a child of the specified type is found
*/
bool VUtil::TraverseDAGForType(MObject& root, MFn::Type type, MObject& node) {
	return false;
}


/**
	Attempts to a list of objects that are parents of this instance mesh. A mesh
	can be a child instance of many other mesh objects so it is therefore nessisary
	to return a list for these circumstances.

	@author Dacre Denny
	@param meshInstance the mesh object who's parents are being aquired
	@param meshInstanceParents the object array where instance parents of the child mesh are stored

	@return true if parent mesh objects are found and returned for the specified mesh instance, otherwise false
*/
bool VUtil::GetInstanceMeshParents(MObject& meshInstance, MObjectArray& meshInstanceParents) {

	if(!VUtil::IsMesh(meshInstance))
		return false;
	
	MString meshInstanceName;
	if(!VUtil::GetObjectName(meshInstance, meshInstanceName))
		return false;

	MItDependencyNodes sceneMeshIterator(MFn::kMesh);
	for(; !sceneMeshIterator.isDone(); sceneMeshIterator.next()) {
		
		MObject sceneMesh = sceneMeshIterator.item();

		MPlug instanceChildrenPlug;
		if(!VUtil::GetObjectAttribute(sceneMesh, MString("INST_CHILDREN"), instanceChildrenPlug))
			continue;

		MString childrenList;
		MAYA_CONTINUE_IF_FAIL(instanceChildrenPlug.getValue(childrenList));

		MStringArray children;
		MAYA_CONTINUE_IF_FAIL(childrenList.split(',', children));

		for(uint i = 0; i < children.length(); i++) {

			if(children[i] == meshInstanceName) {
				MAYA_RETURN_IF_FAIL(meshInstanceParents.append(sceneMesh), false);
				break;
			}
		}
	}

	return true;
}

/**
	Attempts to aquire the first node in the root's connection tree that
	is discovered to equal the function type specified.

	@author Dacre Denny
	@param root the root object that is suspected to have a child node of the specified type
	@param attributeName the name of the attribute to aquire as an object
	@param attributePlug the plug of the result if it is found

	@return true if the attribute on the specified root is found
*/
bool VUtil::GetObjectAttribute(MObject& root, MString& attributeName, MPlug& attributePlug) {
	
	if(attributeName.length() == 0)
		return false;

	MStatus result;
	MFnDependencyNode rootDag = MFnDependencyNode(root, &result);
	MAYA_RETURN_IF_FAIL(result, false)
		
	attributePlug = rootDag.findPlug(attributeName, true, &result);
	MAYA_RETURN_IF_FAIL(result, false)
	return true;
}

/**
	Attempts to aquire the name of the specified object.

	@author Dacre Denny
	@param object the object who's name is being aquired
	@param objectName the name of the specified object

	@return true if the name is successfully aquired
*/
bool VUtil::GetObjectName(MObject& object, MString& objectName) {

	MStatus result;
	MFnDependencyNode objectDN = MFnDependencyNode(object, &result);
	MAYA_RETURN_IF_FAIL(result, false)
	
	objectName = objectDN.name();
	return true;
}

/**
	Attempts to aquire the first decal texture object in the material. If
	material is not a material this function will fail.
	
	@author Dacre Denny
	@param material the material object that is being searched for the texture
	@param textureObject the object of the result if it is found

	@return true if the texture on the specified material is found
*/
bool VUtil::GetMaterialDecal(MObject& material, MObject& textureObject) {

	if(!VUtil::IsMaterialSupported(material))
		return false;

	MStatus result;
	MFnDependencyNode materialDN = MFnDependencyNode(material, &result);
	MAYA_RETURN_IF_FAIL(result, false)

	MPlug targetPlug = materialDN.findPlug(MString("color"), &result);
	MAYA_RETURN_IF_FAIL(result, false)
		
	MPlugArray plugArray;
	targetPlug.connectedTo(plugArray, true, true, &result);
	MAYA_RETURN_IF_FAIL(result, false)

	for(u_int i = 0; i < plugArray.length(); i++) {

		MFnDependencyNode plugDN = MFnDependencyNode(plugArray[i].node(), &result);
		MAYA_CONTINUE_IF_FAIL(result)

		if(plugDN.object().hasFn(MFn::kFileTexture)) {
	
			MPlug filenamePlug = plugDN.findPlug(MString("fileTextureName"), &result);
			MAYA_CONTINUE_IF_FAIL(result)

			textureObject = filenamePlug.node();
			return true;
		}
	}

	return false;
}

/**
	Attempts to aquire the first decal normal object in the material. If
	material is not a material this function will fail.
	
	@author Dacre Denny
	@param material the material object that is being searched for the texture
	@param textureObject the object of the result if it is found

	@return true if the texture on the specified material is found
*/
bool VUtil::GetMaterialNormal(MObject& material, MObject& textureObject) {

	if(!VUtil::IsMaterialSupported(material))
		return false;

	MStatus result;
	MFnDependencyNode materialDN = MFnDependencyNode(material, &result);
	MAYA_RETURN_IF_FAIL(result, false)

	MPlug targetPlug = materialDN.findPlug(MString("normalCamera"), &result);
	MAYA_RETURN_IF_FAIL(result, false)
		
	MPlugArray plugArray;
	targetPlug.connectedTo(plugArray, true, true, &result);
	MAYA_RETURN_IF_FAIL(result, false)

	for(u_int i = 0; i < plugArray.length(); i++) {

		MFnDependencyNode plugDN = MFnDependencyNode(plugArray[i].node(), &result);
		MAYA_CONTINUE_IF_FAIL(result)

		if(plugDN.object().hasFn(MFn::kBump)) {
	
			MFnDependencyNode bumpDN = MFnDependencyNode(plugDN.object(), &result);
			MAYA_CONTINUE_IF_FAIL(result)
	
			MPlug plugBump = bumpDN.findPlug(MString("bumpValue"), &result);
			MAYA_CONTINUE_IF_FAIL(result)
	
			MPlugArray plugBumpArray;
			plugBump.connectedTo(plugBumpArray, true, true, &result);
			MAYA_CONTINUE_IF_FAIL(result)
	
			for(u_int j = 0; j < plugBumpArray.length(); j++) {

				MFnDependencyNode textureDN = MFnDependencyNode(plugBumpArray[j].node(), &result);
				MAYA_CONTINUE_IF_FAIL(result)
				
				if(textureDN.object().hasFn(MFn::kFileTexture)) {
					textureObject = textureDN.object();
					return true;
				}
			}
		}
	}

	return false;
}

/**
	Attempts to aquire the first specular texture object in the material. If
	material is not a material this function will fail.
	
	@author Dacre Denny
	@param material the material object that is being searched for the texture
	@param textureObject the object of the result if it is found

	@return true if the texture on the specified material is found
*/
bool VUtil::GetMaterialAux(MObject& material, MObject& textureObject) {

	if(!VUtil::IsMaterialSupported(material))
		return false;

	MStatus result;
	MFnDependencyNode materialDN = MFnDependencyNode(material, &result);
	MAYA_RETURN_IF_FAIL(result, false)

	MPlug targetPlug = materialDN.findPlug(MString("transparency"), &result);
	MAYA_RETURN_IF_FAIL(result, false)
		
	MPlugArray plugArray;
	targetPlug.connectedTo(plugArray, true, true, &result);
	MAYA_RETURN_IF_FAIL(result, false)

	for(u_int i = 0; i < plugArray.length(); i++) {

		MFnDependencyNode plugDN = MFnDependencyNode(plugArray[i].node(), &result);
		MAYA_CONTINUE_IF_FAIL(result)

		if(plugDN.object().hasFn(MFn::kFileTexture)) {
	
			MPlug filenamePlug = plugDN.findPlug(MString("fileTextureName"), &result);
			MAYA_CONTINUE_IF_FAIL(result)

			textureObject = filenamePlug.node();
			return true;
		}
	}

	return false;
}

/**
	Attempts to aquire a light object from the current scene w.r.t the specified index.
	If the index is beyond the number of lights in the scene the function returns false.
	
	@author Dacre Denny
	@param index index of the light to aquire if it exists
	@param object the light object returned if it exists

	@return true if the light exists for specified index otherwise false
*/
bool VUtil::GetSceneLight(uint index, MObject& object) {
	
	MFn::Type lightTypes[] = {MFn::kPointLight, MFn::kSpotLight, MFn::kDirectionalLight};

	for(u_int i = 0; i < 4; i++) {
		
		MItDependencyNodes lightIterator(lightTypes[i]);
		for(; !lightIterator.isDone(); lightIterator.next()) {

			if(index == 0) {
			
				object = lightIterator.item();
				return true;				
			}
	
			index--;
		}
	}

	return false;
}

/**
	Attempts to aquire all light objects from the current scene.
	
	@author Dacre Denny
	@param lights the object array storing handles to any discovered lights

	@return true
*/
bool VUtil::GetSceneLights(MObjectArray& lights) {
	
	MFn::Type lightTypes[] = {MFn::kPointLight, MFn::kSpotLight, MFn::kDirectionalLight};
	for(u_int i = 0; i < 4; i++) {
		
		for(MItDependencyNodes lightIterator(lightTypes[i]); !lightIterator.isDone(); lightIterator.next())
			lights.append(lightIterator.item());
	}

	return true;
}

/**
	Attempts to aquire all light objects from the current scene.
	
	@author Dacre Denny
	@param meshes the object array storing handles to any discovered meshes

	@return true
*/
bool VUtil::GetSceneMeshes(MObjectArray& meshes) {
	
	return GetSceneObjects(meshes, MFn::kMesh);
}

/**
	Attempts to aquire all light objects from the current scene.
	
	@author Dacre Denny
	@param meshes the object array storing handles to any discovered meshes

	@return true
*/
bool VUtil::GetSceneSamplers(MObjectArray& samplers) {
	
	MFn::Type samplerTypes[] = {MFn::kChecker, MFn::kNoise, MFn::kGrid, MFn::kRamp, MFn::kFileTexture};
	for(u_int i = 0; i < 5; i++) {
		
		for(MItDependencyNodes samplerIterator(samplerTypes[i]); !samplerIterator.isDone(); samplerIterator.next())
			samplers.append(samplerIterator.item());
	}

	return true;
}

/**
	Attempts to aquire all objects from the current scene of the specified type.
	
	@author Dacre Denny
	@param objects the object array storing handles to any matching object types discovered
	@return true
*/
bool VUtil::GetSceneObjects(MObjectArray& objects, MFn::Type type) {

	for(MItDependencyNodes typeIterator(type); !typeIterator.isDone(); typeIterator.next())
		objects.append(typeIterator.item());

	return true;
}

/**
	Attempts to aquire the texture filename for an object that has a texture filename
	function set.
	
	@author Dacre Denny
	@param texture the texture object that is being searched for the texture filename
	@param filename the object that stores the filename if it is found

	@return true if the filename of the specified texture object is found
*/
bool VUtil::GetTextureFilename(MObject& texture, MString& filename) {

	if(!texture.hasFn(MFn::kFileTexture))
		return false;

	/*
	CONSIDER THIS:


	   case MFn::kFileTexture: {

        textureFilenamePlug = texture.findPlug(MString("fileTextureName"), &status);
        if(MAYA_SUCCESS(status)) {
                if(MAYA_SUCCESS(textureFilenamePlug.getValue(textureFilename))) {
                        *pFilename = textureFilename;
                        break;
                }
        
	*/

	MStatus result;
	MFnDependencyNode textureFilenameDN = MFnDependencyNode(texture, &result);
	MAYA_RETURN_IF_FAIL(result, false);

	MPlug textureFilenamePlug = textureFilenameDN.findPlug("fileTextureName", &result);
	MAYA_RETURN_IF_FAIL(result, false);

	return MAYA_SUCCESS(textureFilenamePlug.getValue(filename));
}

/**
	Attempts to aquire the attribute value for an object
	
	@author Dacre Denny
	@param target the object to query the attribute of
	@param label the label of the attribute being queired
	@param out external storage of attribute if found
	@return true if succssfully found otherwise false
*/
bool VUtil::GetAttribute(MObject& target, MString& label, MString& out) {

	MStatus result;
	MFnDependencyNode depNode(target, &result);
	MAYA_RETURN_IF_FAIL(result, false);

	MFnTypedAttribute attribute;
	MObject attObj;

	if(depNode.hasAttribute(label, &result) == false)
		return false;

	attObj = depNode.attribute(label, &result);
	MAYA_RETURN_IF_FAIL(result, false);

	MPlug plug(target, attObj);
	MAYA_RETURN_IF_FAIL(plug.getValue(out), false);
	
	return true;
}

/**
	Attempts to set the attribute value for an object
	
	@author Dacre Denny
	@param target the object to assign the attribute to
	@param label the label of the attribute being added
	@param autoAdd if the attribute is not present on the object, should the function add it
	@return true if succssful otherwise false
*/
bool VUtil::SetAttribute(MObject& target, MString& label, MString& value, bool autoAdd) {
			
	MStatus result;
	MFnDependencyNode depNode(target, &result);
	MAYA_RETURN_IF_FAIL(result, false);

	MFnTypedAttribute attribute;
	MObject attObj;

	if(depNode.hasAttribute(label, &result) == false) {

		if(autoAdd) {
		 
			 attObj = attribute.create(label, label, MFnData::kString, &result);
			 MAYA_RETURN_IF_FAIL(result, false);

			 attribute.setStorable(true);
			 attribute.setKeyable(false);
			 /*attribute.setHidden(true);*/

		 	MDGModifier modifier;
			MAYA_RETURN_IF_FAIL(modifier.addAttribute(target, attObj), false);
			MAYA_RETURN_IF_FAIL(modifier.doIt(), false);
		}
	} else {

		attObj = depNode.attribute(label, &result);
		MAYA_RETURN_IF_FAIL(result, false);
	}

	MPlug plug(target, attObj);
	MAYA_RETURN_IF_FAIL(plug.setValue(value), false);

	return true;
}

/**
	Attempts to remove the attribute from an object
	
	@author Dacre Denny
	@param target the object to remove the attribute from
	@param label the label of the attribute being removed
	@return true if succssful otherwise false
*/
bool VUtil::RemoveAttribute(MObject& target, MString& label) {
			
	MStatus result;
	MFnDependencyNode depNode(target, &result);
	MAYA_RETURN_IF_FAIL(result, false);

	if(depNode.hasAttribute(label, &result) == false) {
	
		MObject attObj = depNode.attribute(label, &result);
		MAYA_RETURN_IF_FAIL(result, false);
		MAYA_RETURN_IF_FAIL(depNode.removeAttribute(attObj), false);
	}

	return true;
}

bool VUtil::GetStringArrayAttribute(MObject& target, MString& label, MStringArray& value) {

	if(label.length() == 0)
		return false;

	MStatus result;
	MFnDependencyNode rootDag = MFnDependencyNode(target, &result);
	MAYA_RETURN_IF_FAIL(result, false)
		
	MPlug attributePlug = rootDag.findPlug(label, true, &result);
	MAYA_RETURN_IF_FAIL(result, false)

	if(!attributePlug.isArray())
		return false;

	unsigned int n = attributePlug.numElements(&result);
	MAYA_RETURN_IF_FAIL(result, false)

	for(uint i = 0; i < n; i++) {
	
		MPlug elementPlug = attributePlug.elementByLogicalIndex(i, &result);
		MAYA_RETURN_IF_FAIL(result, false);

		MString element = elementPlug.asString();
		value.append(element);
	}
	return false;
}

/**
	Attempts to set the value of an attribute array for an object by an array index
	
	@author Dacre Denny
	@param target the object to assign the attribute to
	@param label the label of the attribute being added
	@param index index of the array attribute to set
	@param value string to assign to specified attribute element
	@return true if succssful otherwise false
*/
bool VUtil::SetStringArrayAttribute(MObject& target, MString& label, uint index, MString& value) {

	MString name;
	if(!VUtil::GetObjectName(target, name))
		return false;

	MString cmd = MString("SetStringArrayAttributeElement(\"" + name + "." + label + "\"," + index + ",\"" + value + "\");");
	MAYA_RETURN_IF_FAIL(MGlobal::executeCommand(cmd), false);

	return true;
}

/**
	Attempts to set the attribute value for an object
	
	@author Dacre Denny
	@param target the object to assign the attribute to
	@param label the label of the attribute being added
	@param autoAdd if the attribute is not present on the object, should the function add it
	@return true if succssful otherwise false
*/
bool VUtil::SetAttribute(MObject& target, MString& label, float value, bool autoAdd) {
		
	MStatus result;
	MFnDependencyNode depNode(target, &result);
	MAYA_RETURN_IF_FAIL(result, false);

	MFnTypedAttribute attribute;
	MObject attObj;

	if(depNode.hasAttribute(label, &result) == false) {

		if(autoAdd) {
		 
			 attObj = attribute.create(label, label, MFnData::kNumeric, &result);
			 MAYA_RETURN_IF_FAIL(result, false);

			 attribute.setStorable(true);
			 attribute.setKeyable(false);
			 /*attribute.setHidden(true);*/

		 	MDGModifier modifier;
			MAYA_RETURN_IF_FAIL(modifier.addAttribute(target, attObj), false);
			MAYA_RETURN_IF_FAIL(modifier.doIt(), false);
		}
	} else {

		attObj = depNode.attribute(label, &result);
		MAYA_RETURN_IF_FAIL(result, false);
	}

	MPlug plug(target, attObj);
	MAYA_RETURN_IF_FAIL(plug.setValue(value), false);

	return true;
}

/**
	Attempts to aquire the attribute value for an object
	
	@author Dacre Denny
	@param target the object to query the attribute of
	@param label the label of the attribute being queired
	@param out external storage of attribute if found
	@return true if succssfully found otherwise false
*/
bool VUtil::GetAttribute(MObject& target, MString& label, int& out) {

	MStatus result;
	MFnDependencyNode depNode(target, &result);
	MAYA_RETURN_IF_FAIL(result, false);

	MFnTypedAttribute attribute;
	MObject attObj;

	if(depNode.hasAttribute(label, &result) == false)
		return false;

	attObj = depNode.attribute(label, &result);
	MAYA_RETURN_IF_FAIL(result, false);

	MPlug plug(target, attObj);
	MAYA_RETURN_IF_FAIL(plug.getValue(out), false);
	
	return true;
}

/**
	Attempts to aquire the attribute value for an object
	
	@author Dacre Denny
	@param target the object to query the attribute of
	@param label the label of the attribute being queired
	@param out external storage of attribute if found
	@return true if succssfully found otherwise false
*/
bool VUtil::GetAttribute(MObject& target, MString& label, float& out) {

	float f_out;
	if(VUtil::GetAttribute(target, label, f_out)) {

		int i_out = (int)f_out;
		out = i_out;
		return true;
	}

	out = 0;
	return false;
}

/**
	Attempts to set the attribute value for an object
	
	@author Dacre Denny
	@param target the object to assign the attribute to
	@param label the label of the attribute being added
	@param autoAdd if the attribute is not present on the object, should the function add it
	@return true if succssful otherwise false
*/
bool VUtil::SetAttribute(MObject& target, MString& label, int value, bool autoAdd) {

	return VUtil::SetAttribute(target, label, (float)value, autoAdd);
}
